package earth

import (
	"cgs-server/helper"
	"cgs-server/pkg/set"
	"cgs-server/server"
	"cgs-server/server/assets/mesh"
	"cgs-server/server/base"
	"cgs-server/server/model"
	"errors"
	"fmt"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

type EarthService interface {
	Export(id primitive.ObjectID) ([]*model.ZipFileData, set.Set, error)
	Import(data []*model.ZipFileData, formIdMap bson.M, user *model.User) (string, error)

	base.Service
}

func NewEarthService() EarthService {
	return &earthService{
		earthRepo: NewEarthRepository(),
	}
}

type earthService struct {
	earthRepo EarthRepository
	base.ServiceImpl
}

func (c *earthService) Export(id primitive.ObjectID) ([]*model.ZipFileData, set.Set, error) {
	//压缩
	var zipData []*model.ZipFileData
	// formIds
	formIdSet := set.NewSet()
	//获取projectID
	projectId, _ := c.earthRepo.FindByProjectId(id)

	sceneID, err := projectId.GetSceneId()
	if err != nil {
		return nil, formIdSet, err
	}
	sceneId, _ := primitive.ObjectIDFromHex(sceneID)

	//sceneJson数据
	scene, _ := c.earthRepo.FindBySceneId(sceneId)
	res, err := helper.ToJSON(scene)
	if err != nil {
		return nil, formIdSet, err
	}

	//将数据写入文件
	if len(res) > 0 {
		zipData = append(zipData, &model.ZipFileData{
			Name: server.ExportEditorFile + "scene.json",
			Body: res,
		})
	} else {
		return zipData, formIdSet, nil
	}

	//遍历表单导出模型文件 和 绑定的表单数据
	for _, value := range scene.Data {
		// geojson 绑定的表单
		if bindFormId, ok := value["bindFieldsWorksheetId"]; ok {
			if bindFormId.(string) != "" {
				formIdSet.Add(bindFormId)
			}
		}

		url1, ok := value["url"]
		if !ok {
			continue
		}
		modelId1, ok := value["modelId"]
		modelId := ""
		if ok {
			modelId = modelId1.(string)
		}
		url := url1.(string)
		if !strings.HasPrefix(url, "/Upload/") {
			continue
		}

		if modelId == "" {
			modelId = strings.ReplaceAll(url, "/", "_")
		}

		meshService := mesh.NewMeshService()
		meshData, err := meshService.FindMeshByUrl(url)
		if err != nil {
			return nil, formIdSet, err
		}
		if meshData == nil {
			return nil, formIdSet, fmt.Errorf("Mesh '%s' not found.", url)
		}
		physicalPath := server.MapPath(meshData.SavePath)
		tempPath := filepath.Join(physicalPath, "temp", meshData.FileName)

		jsonFile := []byte{}
		if _, err := os.Stat(tempPath); os.IsNotExist(err) {
			// 如果压缩包不存在就压缩模型文件
			filePath := physicalPath + string(filepath.Separator) // 模型文件夹路径
			file, err := base.ZipFileToBuffer(filePath)
			if err != nil {
				return nil, formIdSet, err
			}
			jsonFile = file.Bytes()
		} else {
			// 如果压缩包存在，就直接读取zip文件
			jsonFile, err = ioutil.ReadFile(tempPath)
			if err != nil {
				return nil, formIdSet, err
			}
		}

		if len(jsonFile) > 0 {
			zipData = append(zipData, &model.ZipFileData{
				Name: server.ExportEditorFile + "model/" + modelId + "/" + meshData.FileName,
				Body: jsonFile,
			})
		}
	}
	return zipData, formIdSet, nil
}

func (c *earthService) Import(data []*model.ZipFileData, formIdMap bson.M, user *model.User) (string, error) {
	sceneID := primitive.NewObjectID()
	for _, value := range data {
		if strings.HasSuffix(value.Name, "scene.json") {
			var earth Earth
			if err := helper.FromJSON(value.Body, &earth); err != nil {
				return "", err
			}
			earth.SetId(sceneID)
			//earth.Name += "_copy"
			earth.TenantId = user.GetTenantId()

			// 处理表单和模型
			meshes := []string{}
			for _, earthMap := range earth.Data {
				if bindFormId, ok := earthMap["bindFieldsWorksheetId"]; ok {
					if formId, ok := formIdMap[bindFormId.(string)]; ok {
						earthMap["bindFieldsWorksheetId"] = formId
					} else {
						earthMap["bindFieldsWorksheetId"] = ""
					}
				}

				// 模型
				url1, ok := earthMap["url"]
				if !ok {
					continue
				}
				modelId1, ok := earthMap["modelId"]
				modelId := ""
				if ok {
					modelId = modelId1.(string)
				}
				url := url1.(string)
				if !strings.HasPrefix(url, "/Upload/") {
					continue
				}

				if modelId == "" {
					modelId = strings.ReplaceAll(url, "/", "_")
				}

				name := earthMap["name"].(string)

				// 上传模型，修改 url 和 modelId
				fmt.Println(modelId)
				for _, modelData := range data {
					if strings.Contains(modelData.Name, modelId) {

						// 标识模型是否已经导入
						flag := false
						for _, meshValue := range meshes {
							if meshValue == modelId {
								flag = true
							}
						}

						if flag {
							break
						}

						file := mesh.ZipFile{
							Filename: name + ".zip",
							Size:     int64(len(modelData.Body)),
							FileType: "application/x-zip-compressed",
							Content:  modelData.Body,
						}
						meshes = append(meshes, modelId)
						meshService := mesh.NewMeshService()
						doc, err := meshService.UploadMesh(&file, user)
						if err != nil {
							return "", err
						}
						earthMap["url"] = doc["Url"]
						earthMap["modelId"] = doc["_id"].(primitive.ObjectID).Hex()
					}
				}
			}

			err := c.earthRepo.Insert(&model.DataUserModel{
				Data: &earth,
				User: user,
			})

			if err != nil {
				return "", err
			}
		}
	}
	return sceneID.Hex(), nil
}

func (c *earthService) Save(mdl *model.DataUserModel) error {
	if earth, ok := mdl.Data.(*Earth); ok {
		if earth.Name == "" {
			return errors.New("name is required")
		}
		// 1. 判断id是否存在，存在就修改，否则就新增
		isAdd := false
		if earth.ID.IsZero() {
			isAdd = true
			earth.ID = primitive.NewObjectID()
		} else {
			isAdd = false
		}

		var err error
		if isAdd {
			err = c.earthRepo.Insert(mdl)
		} else {
			err = c.earthRepo.Update(mdl)
		}
		return err
	} else {
		return errors.New("CgsProj type error")
	}
}

func (c *earthService) Remove(mdl *model.DataUserModel) error {
	if earth, ok := mdl.Data.(*Earth); ok {
		if earth.ID.IsZero() {
			return errors.New("CgsProj ID is empty")
		}
		err := c.earthRepo.Delete(mdl)
		return err
	} else {
		return errors.New("CgsProj type error")
	}
}

func (c *earthService) RemoveLogic(mdl *model.DataUserModel) error {
	if earth, ok := mdl.Data.(*Earth); ok {
		if earth.ID.IsZero() {
			return errors.New("CgsProj ID is empty")
		}
		err := c.earthRepo.DeleteLogic(mdl)
		return err
	} else {
		return errors.New("CgsProj type error")
	}
}

func (c *earthService) GetOne(mdl *model.DataUserModel) (interface{}, error) {
	if earth, ok := mdl.Data.(*Earth); ok {
		if earth.ID.IsZero() {
			return nil, errors.New("earth ID is empty")
		}
		result, err := c.earthRepo.FindOne(mdl)
		return result, err
	} else {
		return nil, errors.New("earth type error")
	}
}
